package com.peas.platform.module.icims.contract.initial.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.peas.platform.framework.mybatisplus.domain.BaseDomain;
import com.peas.platform.framework.mybatisplus.po.BasePo;
import com.peas.platform.framework.mybatisplus.service.BaseServiceImpl;
import com.peas.platform.module.icims.base.approve.component.ApproveCallback;
import com.peas.platform.module.icims.base.approve.dto.ApproveDto;
import com.peas.platform.module.icims.base.approve.dto.ApproveOrderNodeDto;
import com.peas.platform.module.icims.base.approve.enums.ApproveOrderBusinessType;
import com.peas.platform.module.icims.base.approve.service.ApproveOrderService;
import com.peas.platform.module.icims.base.approve.service.ApproveService;
import com.peas.platform.module.icims.contract.initial.api.resp.ContractPayableResp;
import com.peas.platform.module.icims.contract.initial.api.resp.ContractReceiptPaymentResp;
import com.peas.platform.module.icims.contract.initial.api.resp.ContractReceivableResp;
import com.peas.platform.module.icims.contract.initial.controller.vo.ContractInitialCashVo;
import com.peas.platform.module.icims.contract.initial.domain.ContractInitialCashDomain;
import com.peas.platform.module.icims.contract.initial.dto.ContractInitialCashDto;
import com.peas.platform.module.icims.contract.initial.dto.ContractInitialDto;
import com.peas.platform.module.icims.contract.initial.enums.ContractInitialCashState;
import com.peas.platform.module.icims.contract.initial.enums.ContractInitialState;
import com.peas.platform.module.icims.contract.initial.service.ContractInitialCashService;
import com.peas.platform.module.icims.contract.initial.service.ContractInitialProcessService;
import com.peas.platform.module.icims.contract.initial.service.ContractInitialService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;

@Service
public class ContractInitialCashServiceImpl extends BaseServiceImpl<ContractInitialCashDto> implements ContractInitialCashService, ApproveCallback {

    @Resource
    private ContractInitialCashDomain contractInitialCashDomain;
    @Resource
    private ContractInitialService contractInitialService;
    @Resource
    private ContractInitialProcessService contractInitialProcessService;
    @Resource
    private ApproveService approveService;
    @Resource
    private ApproveOrderService approveOrderService;

    @Override
    public BaseDomain<ContractInitialCashDto, ? extends BasePo> getDomain() {
        return contractInitialCashDomain;
    }

    @Override
    public ContractReceivableResp getContractReceivable() {
        DateTime now = DateTime.now();
        ContractReceivableResp r = new ContractReceivableResp();

        ContractInitialCashDto c1 = new ContractInitialCashDto();
        c1.setCashType("1");
        c1.setState("1");
        BigDecimal v1 = this.sum("cashAmount", c1);
        r.setValue1(NumberUtil.nullToZero(v1));

        ContractInitialCashDto c2 = new ContractInitialCashDto();
        c2.setCashType("1");
        c2.setState("1");
        c2.setCreateTimeStart(DateUtil.beginOfMonth(now));
        c2.setCreateTimeEnd(DateUtil.endOfMonth(now));
        BigDecimal v2 = this.sum("cashAmount", c2);
        r.setValue2(NumberUtil.nullToZero(v2));

        ContractInitialCashDto c3 = new ContractInitialCashDto();
        c3.setCashType("1");
        c3.setState("4");
        c3.setCreateTimeStart(DateUtil.beginOfMonth(now));
        c3.setCreateTimeEnd(DateUtil.endOfMonth(now));
        BigDecimal v3 = this.sum("cashAmount", c3);
        r.setValue3(NumberUtil.nullToZero(v3));

        ContractInitialCashDto c4 = new ContractInitialCashDto();
        c4.setCashType("1");
        c4.setState("1");
        c4.setCreateTimeStart(DateUtil.beginOfYear(now));
        c4.setCreateTimeEnd(DateUtil.endOfYear(now));
        BigDecimal v4 = this.sum("cashAmount", c4);
        r.setValue4(NumberUtil.nullToZero(v4));

        ContractInitialCashDto c5 = new ContractInitialCashDto();
        c5.setCashType("1");
        c5.setState("4");
        c5.setCreateTimeStart(DateUtil.beginOfYear(now));
        c5.setCreateTimeEnd(DateUtil.endOfYear(now));
        BigDecimal v5 = this.sum("cashAmount", c5);
        r.setValue5(NumberUtil.nullToZero(v5));
        return r;
    }

    @Override
    public ContractPayableResp getContractPayable() {
        DateTime now = DateTime.now();
        ContractPayableResp r = new ContractPayableResp();

        ContractInitialCashDto c1 = new ContractInitialCashDto();
        c1.setCashType("2");
        c1.setState("1");
        r.setValue1(this.sum("cashAmount", c1));

        ContractInitialCashDto c2 = new ContractInitialCashDto();
        c2.setCashType("2");
        c2.setState("1");
        c2.setCreateTimeStart(DateUtil.beginOfMonth(now));
        c2.setCreateTimeEnd(DateUtil.endOfMonth(now));
        r.setValue2(this.sum("cashAmount", c2));

        ContractInitialCashDto c3 = new ContractInitialCashDto();
        c3.setCashType("2");
        c3.setState("4");
        c3.setCreateTimeStart(DateUtil.beginOfMonth(now));
        c3.setCreateTimeEnd(DateUtil.endOfMonth(now));
        r.setValue3(this.sum("cashAmount", c3));

        ContractInitialCashDto c4 = new ContractInitialCashDto();
        c4.setCashType("2");
        c4.setState("1");
        c4.setCreateTimeStart(DateUtil.beginOfYear(now));
        c4.setCreateTimeEnd(DateUtil.endOfYear(now));
        r.setValue4(this.sum("cashAmount", c4));

        ContractInitialCashDto c5 = new ContractInitialCashDto();
        c5.setCashType("2");
        c5.setState("4");
        c5.setCreateTimeStart(DateUtil.beginOfYear(now));
        c5.setCreateTimeEnd(DateUtil.endOfYear(now));
        r.setValue5(this.sum("cashAmount", c5));
        return r;
    }

    @Override
    public ContractReceiptPaymentResp getContractReceiptPayment(Integer year, String type) {
        Assert.notNull(year, "年度不能为空");
        Assert.notBlank(type, "收付类型不能为空");
        ContractInitialCashDto c = new ContractInitialCashDto();
        c.setCreateTimeStart(DateUtil.beginOfYear(DateUtil.parse(String.valueOf(year), DatePattern.NORM_YEAR_PATTERN)));
        c.setCreateTimeEnd(DateUtil.endOfYear(DateUtil.parse(String.valueOf(year), DatePattern.NORM_YEAR_PATTERN)));
        c.setCashType(type);
        List<ContractInitialCashDto> list = this.list(c);
        ContractReceiptPaymentResp r = new ContractReceiptPaymentResp();
        if (CollUtil.isEmpty(list)) {
            return r;
        }
        r.setBarData(Arrays.asList(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO));
        r.setLineData(Arrays.asList(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO));
        for (ContractInitialCashDto dto : list) {
            String month = DateUtil.format(dto.getCreateTime(), "MM");
            switch (month) {
                case "01": {
                    int idx = 0;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "02": {
                    int idx = 1;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "03": {
                    int idx = 2;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "04": {
                    int idx = 3;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "05": {
                    int idx = 4;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "06": {
                    int idx = 5;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "07": {
                    int idx = 6;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "08": {
                    int idx = 7;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "09": {
                    int idx = 8;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "10": {
                    int idx = 9;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "11": {
                    int idx = 10;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
                case "12": {
                    int idx = 11;
                    BigDecimal v = r.getBarData().get(idx).add(NumberUtil.nullToZero(dto.getCashAmount()));
                    r.getBarData().set(idx, v);
                    break;
                }
            }
        }
        ContractInitialCashDto c1 = new ContractInitialCashDto();
        c1.setCreateTimeStart(DateUtil.beginOfMonth(DateUtil.parse((year - 1) + "-12", DatePattern.NORM_MONTH_PATTERN)));
        c1.setCreateTimeEnd(DateUtil.endOfMonth(DateUtil.parse((year - 1) + "-12", DatePattern.NORM_MONTH_PATTERN)));
        c1.setCashType(type);
        List<ContractInitialCashDto> list1 = this.list(c1);
        BigDecimal prevous = BigDecimal.ZERO;
        if (CollUtil.isNotEmpty(list1)) {
            for (ContractInitialCashDto dto : list) {
                prevous = prevous.add(NumberUtil.nullToZero(dto.getCashAmount()));
            }
        }
        for (int i = 0; i < 12; i++) {
            BigDecimal curr = r.getBarData().get(i);
            BigDecimal pre = prevous;
            if (i > 0) {
                pre = r.getBarData().get(i - 1);
            }
            if (NumberUtil.equals(pre, BigDecimal.ZERO)) {
                r.getLineData().set(i, BigDecimal.ZERO);
            } else {
                BigDecimal line = (curr.subtract(pre)).divide(pre, 2, RoundingMode.HALF_UP);
                r.getLineData().set(i, line);
            }
        }
        return r;
    }

    @Transactional
    @Override
    public void cashContractInitial(ContractInitialDto dto, Long approveId, List<ApproveOrderNodeDto> approveNodes, List<ContractInitialCashVo> items) {
        ApproveDto ad = approveService.getById(approveId);
        ApproveOrderBusinessType approveOrderBusinessType = ApproveOrderBusinessType.contractCash;
        Assert.notNull(approveOrderBusinessType, "合同数据错误");
        items.forEach(v -> {
            Assert.notNull(v.getId(), "ID不能为空");
            ContractInitialCashDto item = BeanUtil.copyProperties(v, ContractInitialCashDto.class);
            item.setState(ContractInitialCashState.submit.getKey());
            this.updateById(item);
            String desc = "合同编码(" + dto.getCode() + ")";
            approveOrderService.createApproveOrder(ad, approveNodes, approveOrderBusinessType, item.getId(), desc, null, this.getClass());
        });
    }

    @Transactional
    @Override
    public void revocationContractInitialCash(Long id) {
        ContractInitialCashDto dto = this.getById(id);
        Assert.notNull(dto, "找不到数据");
        Assert.equals(dto.getState(), ContractInitialCashState.submit.getKey(), "单据状态不是[{}]", ContractInitialCashState.submit.getValue());
        dto.setState(ContractInitialCashState.revocation.getKey());
        this.updateById(dto);
        ApproveOrderBusinessType approveOrderBusinessType = ApproveOrderBusinessType.contractCash;
        Assert.notNull(approveOrderBusinessType, "合同数据错误");
        approveOrderService.revocationApproveOrder(approveOrderBusinessType, id);
    }

    @Transactional
    @Override
    public void callbackAfterApprove(Long businessId, boolean isPass, boolean isFinalPass) {
        if (businessId == null)
            return;
        if (isPass) {
            if (isFinalPass) {
                ContractInitialCashDto dto = this.getById(businessId);
                dto.setState(ContractInitialCashState.pass.getKey());
                this.updateById(dto);
                ContractInitialDto cid = contractInitialService.getById(dto.getContractInitialId());
                cid.setState(ContractInitialState.payed.getKey());
                List<ContractInitialCashDto> list = this.listByField("contractInitialId", cid.getId());
                long notOver = list.stream().filter(item -> !StrUtil.equals(item.getState(), ContractInitialCashState.pass.getKey())).count();
                if (notOver > 0) {
                    cid.setState(ContractInitialState.paying.getKey());
                }
                contractInitialService.updateById(cid);
                contractInitialProcessService.saveContractInitialProcessDto(cid, ContractInitialState.paying);
            }
        } else {
            ContractInitialCashDto dto = this.getById(businessId);
            dto.setState(ContractInitialCashState.reject.getKey());
            this.updateById(dto);
        }
    }

}
